/**
 *	@file	mempool.c
 *	@brief	the mempool implement, it provide free object for other function
 *		It's only used in fixed size object.
 *
 *	@author	FZ
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>

#include "mempool.h"

/**
 *	Define some MACRO for printing error message
 */
#define _MEMPOOL_DEBUG
#ifdef _MEMPOOL_DEBUG
#define	_MP_DBG(fmt, args...)	printf("[mempool]: "fmt, ##args)
#else
#define _MP_DBG(fmt, args...)
#endif

#define _MP_ERR(fmt, args...)	fprintf(stderr, "[mempool-err]:%s:%d: "fmt, \
					__FILE__, __LINE__, ##args)

/* memory pool align to 16, and the max failed time is 4 */
#define _MP_ALIGN	16
#define	_MP_MAX_FAILURE	4

static void * 
_mp_alloc_block(mempool_t *pool, size_t n)
{

	return NULL;
}

static void * 
_mp_alloc_large(mempool_t *pool, size_t n)
{
	return NULL;
}

mempool_t * 
mempool_alloc(size_t size)
{
	void *ptr = NULL;
	mempool_t *p = NULL;

	if (size < 1)
		return NULL;

	if (posix_memalign(&ptr, _MP_ALIGN, (size + sizeof(mempool_t))))
		_MP_ERR("malloc mempool error: %s\n", strerror(errno));
		return NULL;
	}

	p = (mempool_t *)ptr;
	p->d.last = (u_int8_t *)pool + sizeof(mempool_t);
	p->d.end = (u_int8_t *)pool + size;
	p->d.next = NULL;
	p->d.failed = 0;

	p->max = size;
	p->current = p;
	p->large = NULL;

	return p;
}

/**
 *	Free memory pool @pool which is alloced using @mempool_alloc
 *
 *	No return.
 */
void 
mempool_free(mempool_t *pool)
{
	mempool_t *p, *n;
	mempool_large_t *l;

	if (!pool)
		return;
	
	/* free large pool */
	for (l = pool->large; l; l = l->next) {
		if (l->alloc)
			free(l->alloc);
	}

	/* free pool */
	for (p = pool; n = pool->d.next; ;p = n; n = n->d.next) {
		free(p);
		if (n == NULL)
			break;
	}
}

/**
 *	Get a freed memory unit from memory pool @pool. If there is no 
 *	freed memory unit, return NULL, else return a freed memory unit.
 *
 *	Return not NULL if success, or NULL on failed.
 */
void * 
mempool_alloc(mempool_t *pool, size_t size)
{
	u_int8_t *m;
	mempool_t *p;

	if (!pool || size < 1)
		return NULL;

	if (size < pool->size) {
		p = pool->current;
		do {
			m = p->d.last;
			if ((size_t)(p->d.end -m) >= size) {
				p->d.last = m + size;
				return m;
			}
			p = p->d.next;
		} while (p);

		return _mp_alloc_block(pool, size);
	}

	return _mp_alloc_large(pool, size);
}

/**
 *	Put a memory unit @ptr into memory pool @pool. It'll check
 *	the memory unit to see it's origin from this pool.
 *
 *	Return 0 if success, -1 means memory unit is invalid.
 */
int mempool_put(mempool_t *pool, void *ptr)
{
	_mempool_node_t *node = NULL;
	int ret = -1;

	if (!pool || !ptr)
		return -1;
	
	node = _MEMPOOL_OFFSET(ptr);

	_mempool_lock(pool);
	
	/* check magic number and flags */
	if (node->magic != pool->magic) {
		_MP_ERR("magic number is invalid\n");
		goto OUT;
	}

	if (!_MEMPOOL_IS_USED(node->flags)) {
		_MP_ERR("put a freed memory unit\n");
		goto OUT;
	}

	if (node->pool != pool) {
		_MP_ERR("put memory to different pool\n");
		goto OUT;
	}

	node->next = pool->flist;
	pool->flist = node;
	pool->nfreed++;

	_MEMPOOL_CLR_USED(node->flags);

	ret = 0;

OUT:

	_mempool_unlock(pool);
	
	return ret;
}

/**
 *	Put a memory unit into memory pool, the memory pool is hidden
 *      in @data, see implement.
 *
 *	Return 0 if success, -1 on error.
 */
int
mempool_put1(void *ptr)
{
	_mempool_node_t *node = NULL;
	mempool_t *pool = NULL;
	int ret = -1;

	if (!ptr)
		return -1;
	
	node = _MEMPOOL_OFFSET(ptr);
	pool = node->pool;

	if (!pool)
		return -1;

	_mempool_lock(pool);
	
	/* check magic number and flags */
	if (node->magic != pool->magic) {
		_MP_ERR("magic number is invalid\n");
		goto OUT;
	}

	if (!_MEMPOOL_IS_USED(node->flags)) {
		_MP_ERR("put a freed memory unit\n");
		goto OUT;
	}

	if (node->pool != pool) {
		_MP_ERR("put memory to different pool\n");
		goto OUT;
	}

	node->next = pool->flist;
	pool->flist = node;
	pool->nfreed++;

	_MEMPOOL_CLR_USED(node->flags);

	ret = 0;

OUT:

	_mempool_unlock(pool);
	
	return ret;

}

/**
 *	print the _mempool_node's to stdout
 *
 *	No return.
 */
static void 
_mempool_node_print(const _mempool_node_t *node)
{
	assert(node);
	
	printf("\tnode(%p): magic %u, flags %x, pool %p next %p, size %u\n",
	       node, node->magic, node->flags, node->pool, 
	       node->next,node->size);
}


/**
 *	Print memory pool @pool to stdout
 *
 *	No return.
 */
void mempool_print(mempool_t *pool)
{
	_mempool_node_t *node;

	if (!pool)
		return;

	_mempool_lock(pool);

	printf("pool(%p): magic %u, size %u, pool %p, freed: %u\n",
	       pool, pool->magic, pool->size, pool->pool, pool->nfreed);

	node = pool->flist;
	while (node) {
		_mempool_node_print(node);
		node = node->next;
	}

	_mempool_unlock(pool);
}

